// C source file with the function definitions to handle BTP's TPT1 section

// check_btp_tpt1() function
// function to check if the TPT1 section of a BTP is valid
j3dgraph_btp_tpt1_err_cd check_j3dgraph_btp_tpt1(j3dgraph * file)
{
  // check params
  if (file == NULL)
    return J3DGRAPH_BTP_TPT1_ERR_INV_STRUCT;
  
  // error variable to return a "valid" error, hopefully it returns no error >:]
  // if a fatal error is found the function will return said error immediately
  j3dgraph_btp_tpt1_err_cd soft_err = J3DGRAPH_BTP_TPT1_ERR_NO_ERR;
  
  // temp variables
  j3dgraph_gen_sec * sec = (file -> SEC)[J3DGRAPH_BTP_TPT1_POS];
  byte * ptr = sec -> START;
  ENDIAN_T endian = file -> ENDIAN;
  umax tmp = 0;
  
  // header
  if (comp_bytes(ptr, J3DGRAPH_BTP_SEC_START[J3DGRAPH_BTP_TPT1_POS][endian - 1], 4) == 0)
    return J3DGRAPH_BTP_TPT1_ERR_HEADER;
  ptr += 8; // skip section size (it was already checked)  
  // loop mode
  if (get_std_uint(8, ptr, endian) > 4)
    soft_err = get_min_uint(J3DGRAPH_BTP_TPT1_ERR_LOOP_MD, soft_err);
  ptr++;  
  // unknown 1
  if (get_std_uint(8, ptr, endian) != J3DGRAPH_BTP_TPT1_UNK_1)
    soft_err = get_min_uint(J3DGRAPH_BTP_TPT1_ERR_UNK_1, soft_err);
  ptr++;
    
  // animation lenght can be any uint16 value
  /* umax anim_length = get_std_uint(16, ptr, endian); */ ptr += 2;  
  // save animated material count for later
  umax anim_anim_mat_cnt = get_std_uint(16, ptr, endian); ptr += 2;  
  // texture swap table length can be anything afaik
  // but I will set a warnning code if it does not match
  // the sum of the animation length of each material
  umax tex_swp_tb_length = get_std_uint(16, ptr, endian); ptr += 2;
  
  // 4 uint32 offsets incomming
  // SMG does not check the material index table at all, but I will  
  // tpt1 section offset
  if (get_std_uint(32, ptr, endian) != J3DGRAPH_BTP_TPT1_OFFSET)
    return J3DGRAPH_BTP_TPT1_ERR_TPT1_OFFSET;
  ptr += 4;
  // store texture swap table, material index table and material string table offsets for later
  umax tex_swp_tb_offset = get_std_uint(32, ptr, endian); ptr += 4;  
  umax anim_mat_ind_tb_offset = get_std_uint(32, ptr, endian); ptr += 4;  
  umax anim_mat_str_tb_offset = get_std_uint(32, ptr, endian); ptr += 4;
  
  // check the start of the animation table
  tmp = 0;
  for (umax i = 0; i < anim_anim_mat_cnt; i++)
  {
    // get the current material's animation length
    umax cur_anim_length = get_std_uint(16, ptr, endian);    
    if (cur_anim_length == 0)
      soft_err = get_min_uint(J3DGRAPH_BTP_TPT1_ERR_MAT_ANIM_LENGTH, soft_err);
    
    // get the material animation data position on the texture swap table
    umax cur_anim_data_pos = get_std_uint(16, ptr + 2, endian);    
    // the animation data position is outside the texture swap table
    if (cur_anim_data_pos + cur_anim_length > tex_swp_tb_length)
      return J3DGRAPH_BTP_TPT1_ERR_MAT_ANIM_DT_POS;
      
    // unknown 2 is not 0x00
    if (get_std_uint(8, ptr + 4, endian) != J3DGRAPH_BTP_TPT1_UNK_2)
      return J3DGRAPH_BTP_TPT1_ERR_UNK_2;            
    // unknown 3 is not 0xFFFFFF
    for (byte i = 0; i < 3; i++)
      if (get_std_uint(8, ptr + 5 + i, endian) != 0xFF)
        soft_err = get_min_uint(J3DGRAPH_BTP_TPT1_ERR_UNK_3, soft_err);
    
    tmp += cur_anim_length;
    ptr += 8;
  }
  
  // check the texture swap table length (tmp)
  if (tex_swp_tb_length != tmp)
    return J3DGRAPH_BTP_TPT1_ERR_TEX_TB_LENGTH;
  
  // check the texture swap table offset
  if (tex_swp_tb_offset != (ptr - (sec -> START)))
    return J3DGRAPH_BTP_TPT1_ERR_TEX_TB_OFFSET;
  ptr += 2 * tex_swp_tb_length; // elements on it are 2 bytes long each
  // need the BMD/BDL model to check this table
  
  // skip padding
  ptr += get_j3dgraph_padding_size(ptr - (sec -> START), false);
  
  // check the animated material index table offset
  if (anim_mat_ind_tb_offset != ptr - (sec -> START))
    soft_err = get_min_uint(soft_err, J3DGRAPH_BTP_TPT1_ERR_MAT_IND_TB_OFFSET);
  ptr += 2 * anim_anim_mat_cnt;
  // need the BMD/BDL model to check this table
  
  // skip padding
  ptr += get_j3dgraph_padding_size(ptr - (sec -> START), false);
  
  // check the animated material string table offset
  if (anim_mat_str_tb_offset != ptr - (sec -> START))
    soft_err = get_min_uint(soft_err, J3DGRAPH_BTP_TPT1_ERR_MAT_IND_TB_OFFSET);
  // need the BMD/BDL model to check this table
  
  // check the animated material string table
  tmp = get_std_uint(32, sec -> SIZE, endian); 
  if (check_j3dgraph_str_tb(ptr, (sec -> START) + tmp - ptr, endian) < J3DGRAPH_STR_TB_ERR_NO_ERR)
    return J3DGRAPH_BTP_TPT1_ERR_INV_STR_TB;
    
  // check the animated material table start
  j3dgraph_str_tb * str_tb = get_j3dgraph_str_tb(ptr, (sec -> START) + tmp - ptr, endian);
  if (get_std_uint(16, str_tb -> STR_CNT, endian) != anim_anim_mat_cnt) {
    close_j3dgraph_str_tb(str_tb);
    return J3DGRAPH_BTP_TPT1_ERR_MAT_STR_TB_START;
  }
  // close the structure
  close_j3dgraph_str_tb(str_tb);
    
  // the last padding does not have to be checked
  // whatever is in there makes the section valid
  return soft_err;
}

// append_btp_tpt1() function
// append a btp_tpt1 struct to an incomplete j3dgraph struct
j3dgraph_btp_tpt1 * append_j3dgraph_btp_tpt1(j3dgraph * file)
{
  // check if the section is valid
  j3dgraph_btp_tpt1 * tpt1 = NULL;
  j3dgraph_btp_tpt1_err_cd err_cd = check_j3dgraph_btp_tpt1(file);
  if (err_cd < J3DGRAPH_BTP_TPT1_ERR_NO_ERR)
    goto err;
  
  // otherwise append the structure to j3dgraph
  tpt1 = allocate_memory(1, sizeof(j3dgraph_btp_tpt1));
  if (tpt1 == NULL)
    goto err;
  
  // fill the structure
  // temp variables
  umax tmp = 0;
  j3dgraph_gen_sec * sec = (file -> SEC)[J3DGRAPH_BTP_TPT1_POS];
  byte * ptr = (sec -> START) + 8;
    
  // my additions
  tpt1 -> PATH = file -> PATH;
  tpt1 -> ENDIAN = file -> ENDIAN;
  tpt1 -> ERR_CD = err_cd;
  // actual structure
  tpt1 -> START = sec -> START;
  tpt1 -> SIZE = sec -> SIZE;  
  tpt1 -> LOOP_MD = ptr; ptr++;
  tpt1 -> UNK_1 = ptr; ptr++;
  tpt1 -> ANIM_LENGTH = ptr; ptr += 2;
  tpt1 -> ANIM_MAT_CNT = ptr; ptr += 2;
  tpt1 -> TEX_SWP_TB_LENGTH = ptr; ptr += 2;
  
  // get anim mat count
  umax anim_mat_cnt = get_std_uint(16, tpt1 -> ANIM_MAT_CNT, file -> ENDIAN);
  
  // offsets
  tpt1 -> TPT1_OFFSET = ptr; ptr += 4;
  tpt1 -> TEX_SWP_TB_OFFSET = ptr; ptr += 4;
  tpt1 -> ANIM_MAT_IND_TB_OFFSET = ptr; ptr += 4;
  tpt1 -> ANIM_MAT_STR_TB_OFFSET = ptr; ptr += 4;
  
  // animation table
  tpt1 -> ANIM_TB = ptr; ptr += anim_mat_cnt * 8;
  tpt1 -> TEX_SWP_TB = ptr; ptr += 2 * get_std_uint(16, tpt1 -> TEX_SWP_TB_LENGTH, file -> ENDIAN);
  
  // padding (4)
  tpt1 -> PAD_1 = ptr;
  ptr += get_j3dgraph_padding_size((tpt1 -> PAD_1) - (tpt1 -> START), false);
   
  // material position table
  tpt1 -> ANIM_MAT_IND_TB = ptr; ptr += anim_mat_cnt * 2;
  
  // padding (4)
  tpt1 -> PAD_2 = ptr;
  ptr += get_j3dgraph_padding_size((tpt1 -> PAD_2) - (tpt1 -> START), false);
  
  // create the joint string table
  ptr = (tpt1 -> START) + get_std_uint(32, tpt1 -> ANIM_MAT_STR_TB_OFFSET, file -> ENDIAN);
  tmp = get_j3dgraph_str_tb_size(ptr, (file -> DATA) + (file -> FSIZE) - ptr, file -> ENDIAN);
  if (tmp == 0)
    goto err;
  tpt1 -> ANIM_MAT_STR_TB = get_j3dgraph_str_tb(ptr, (file -> DATA) + (file -> FSIZE) - ptr, file -> ENDIAN);
  if ((tpt1 -> ANIM_MAT_STR_TB) == NULL)
    goto err;
  ptr += tmp;

  // final padding (32)
  tpt1 -> PAD_3 = ptr;
  
  // remove the j3dgraph_gen_sec and append the new one
  free_memory((file -> SEC)[J3DGRAPH_BTP_TPT1_POS]);
  return ((file -> SEC)[J3DGRAPH_BTP_TPT1_POS] = tpt1);
  // invalid section
  err:
  free_memory(tpt1);
  return NULL;
}

// remove_btp_tpt1() function
// function to remove a btp_tpt1 structure from a j3dgraph structure
void remove_j3dgraph_btp_tpt1(j3dgraph * file)
{
  // check params
  if (file == NULL || (file -> SEC) == NULL)
    return;  
  // free the memory allocated by append_btp_tpt1()
  close_j3dgraph_str_tb(((j3dgraph_btp_tpt1 *)
                        (file -> SEC)[J3DGRAPH_BTP_TPT1_POS]) -> ANIM_MAT_STR_TB);
  free_memory((file -> SEC)[J3DGRAPH_BTP_TPT1_POS]);
  return;
}

// print_btp_tpt1() function
// print the information of the TPT1 section of a BTP
void print_j3dgraph_btp_tpt1(j3dgraph_btp_tpt1 * tpt1)
{
  // check ptr
  if (tpt1 == NULL || (tpt1 -> ERR_CD) < J3DGRAPH_BTP_TPT1_ERR_NO_ERR)
    return;
  
  // starting visual queue
  printf("\n### J3DGraph TPT1 section info start ###\n\n");
  
  umax tmp = 0; // temp variable
  
  // file info and error code
  printf("File: %s\n", tpt1 -> PATH);
  printf("Section endian: %s\n", (tpt1 -> ENDIAN) == BIG ? "BIG" : "LITTLE");
  printf("Section error code: %u\n", tpt1 -> ERR_CD);
  
  // section header
  printf("\nSection start: ");
  print_ascii_arr(tpt1 -> START, 4);
  printf("Section size (bytes): %llu\n", get_std_uint(32, tpt1 -> SIZE, tpt1 -> ENDIAN));
  
  // get animated material count
  umax anim_mat_cnt = get_std_uint(16, tpt1 -> ANIM_MAT_CNT, tpt1 -> ENDIAN);
  
  // rest of the section  
  printf("Loop mode: %llu, ", get_std_uint(8, tpt1 -> LOOP_MD, tpt1 -> ENDIAN));
  printf("Unknown 1: 0x%02llX\n", get_std_uint(8, tpt1 -> UNK_1, tpt1 -> ENDIAN));
  
  printf("Animation length (frames): %llu\n", get_std_uint(16, tpt1 -> ANIM_LENGTH, tpt1 -> ENDIAN));
  printf("Animated material count: %llu\n", anim_mat_cnt);
  printf("Texture swap table length: %llu\n", get_std_uint(16, tpt1 -> TEX_SWP_TB_LENGTH, tpt1 -> ENDIAN));
  
  // offsets
  printf("TPT1 section offset: 0x%08llX\n", get_std_uint(32, tpt1 -> TPT1_OFFSET, tpt1 -> ENDIAN));
  printf("Tex swap table offset: 0x%08llX\n", get_std_uint(32, tpt1 -> TEX_SWP_TB_OFFSET, tpt1 -> ENDIAN));
  printf("Anim mat index table offset: 0x%08llX\n",
         get_std_uint(32, tpt1 -> ANIM_MAT_IND_TB_OFFSET, tpt1 -> ENDIAN));
  printf("Anim mat str table offset: 0x%08llX\n",
         get_std_uint(32, tpt1 -> ANIM_MAT_STR_TB_OFFSET, tpt1 -> ENDIAN));
  
  // animation table
  tmp = 0;
  for (umax i = 0; i < anim_mat_cnt; i++, tmp += 8) {
    printf("\nmat[%llu] ", i);
    printf("Anim length: %llu, ", get_std_uint(16, tpt1 -> ANIM_TB + tmp, tpt1 -> ENDIAN));
    printf("Anim data pos: %llu\n", get_std_uint(16, tpt1 -> ANIM_TB + tmp + 2, tpt1 -> ENDIAN));
    printf("Unknown 2: 0x%02llX, ", get_std_uint(8, tpt1 -> ANIM_TB + tmp + 4, tpt1 -> ENDIAN));
    printf("Unknown 3: 0x");
    for (byte i = 0; i < 3; i++)
      printf("%02llX", get_std_uint(8, tpt1 -> ANIM_TB + tmp + 5 + i, tpt1 -> ENDIAN));
  }
  printf("\n");
  
  // texture swap table
  printf("\nTexture swap table (texture indexes):");
  tmp = get_std_uint(16, tpt1 -> TEX_SWP_TB_LENGTH, tpt1 -> ENDIAN);
  for (umax i = 0; i < tmp; i++) {
    if (i % 16 == 0)
      printf("\n[%3llu - %3llu]: ", i, i + 15);
    printf("%2llu ", get_std_uint(16, tpt1 -> TEX_SWP_TB + (i * 2), tpt1 -> ENDIAN));
  }
  printf("\n");
  
  // padding (4)
  printf("\nPadding 1: ");
  print_ascii_arr(tpt1 -> PAD_1, get_j3dgraph_padding_size((tpt1 -> PAD_1) - (tpt1 -> START), false));
  
  // animated material index table
  for (umax i = 0; i < anim_mat_cnt; i++)
    printf("\nmat[%llu] index in MAT3: %llu", i,
           get_std_uint(16, tpt1 -> ANIM_MAT_IND_TB + (2 * i), tpt1 -> ENDIAN));
  putchar('\n');
  
  // padding (4)
  printf("\nPadding 2: ");
  print_ascii_arr(tpt1 -> PAD_2, get_j3dgraph_padding_size((tpt1 -> PAD_2) - (tpt1 -> START), false));
  
  // animated material string table
  printf("\n## Anim material string table begin:\n");
  print_j3dgraph_str_tb(tpt1 -> ANIM_MAT_STR_TB);  
  
  // padding (32)
  printf("\nPadding 3: ");
  print_ascii_arr(tpt1 -> PAD_3, get_j3dgraph_padding_size((tpt1 -> PAD_3) - (tpt1 -> START), false));
    
  // end of structure
  printf("\n### J3DGraph TPT1 section info end ###\n");
  return;
}

// check_j3dgraph_btp_tpt1_agst_bmd_bdl_mat3_tex1() function
// function to check a BTP TPT1 section agaisnt the MAT3 and TEX1 sections
// of a BMD/BDL. In short, to see if a BTP is compatible with a BMD/BDL.
j3dgraph_btp_tpt1_err_cd check_j3dgraph_btp_tpt1_agst_bmd_bdl_mat3_tex1(j3dgraph_btp_tpt1 * tpt1,
                                                                        j3dgraph_bmd_mat3 * mat3,
                                                                        j3dgraph_bmd_tex1 * tex1)
{  
  // check params
  if (tpt1 == NULL || mat3 == NULL || tex1 == NULL
      || (tpt1 -> ERR_CD) < J3DGRAPH_BTP_TPT1_ERR_NO_ERR
      || (mat3 -> ERR_CD) < J3DGRAPH_BMD_MAT3_ERR_NO_ERR
      || (tex1 -> ERR_CD) < J3DGRAPH_BMD_TEX1_ERR_NO_ERR)
    return J3DGRAPH_BTP_TPT1_ERR_INV_STRUCT;
  
  // "valid" error variable to return
  j3dgraph_btp_tpt1_err_cd soft_err = J3DGRAPH_BTP_TPT1_ERR_NO_ERR;
  
  // start the check
  umax tmp = 0; // temp variable
  
  // first check the sections endian
  if ((tpt1 -> ENDIAN != mat3 -> ENDIAN) || (tpt1 -> ENDIAN != tex1 -> ENDIAN))
    soft_err = get_min_uint(soft_err, J3DGRAPH_BTP_TPT1_ERR_DIF_ENDIAN_MODEL);  
  
  // get texture count on TEX1 and check the texture
  // indexes of the texture swap table in TPT1
  umax tex1_tex_count = get_std_uint(16, tex1 -> TEX_CNT, tex1 -> ENDIAN);
  tmp = get_std_uint(16, tpt1 -> TEX_SWP_TB_LENGTH, tpt1 -> ENDIAN);
  for (umax i = 0; i < tmp; i++)
    if (get_std_uint(16, tpt1 -> TEX_SWP_TB + (i * 2), tpt1 -> ENDIAN) > tex1_tex_count - 1)
      return J3DGRAPH_BTP_TPT1_ERR_INV_TEX_INDEX;
      
  // get the material count on MAT3
  // and check TPT1 material count
  umax mat3_mat_cnt = get_std_uint(16, mat3 -> MAT_CNT, mat3 -> ENDIAN);
  umax tpt1_anim_mat_cnt = get_std_uint(16, tpt1 -> ANIM_MAT_CNT, tpt1 -> ENDIAN);
  if (tpt1_anim_mat_cnt > mat3_mat_cnt)
    return J3DGRAPH_BTP_TPT1_ERR_MAT_CNT_OVERFLOW;

  // string comparison time
  char_enc old_enc = CHAR_ARR_ENC_STATE;
  set_ch_arr_enc_state(CHAR_ENC_CP932);

  // check the tpt1 material names against the mat3 material names
  // in the way check the material index table of TPT1
  tmp = 0;
  for (umax i = 0; i < tpt1_anim_mat_cnt; i++)
  {
    // get tpt1 material index on mat3 and the material string pointer
    umax tpt1_anim_mat_ind = get_std_uint(16, tpt1 -> ANIM_MAT_IND_TB + (2 * i), tpt1 -> ENDIAN);
    byte * tpt1_anim_mat_str = (tpt1 -> ANIM_MAT_STR_TB -> STR_CNT) + get_std_uint(16, tpt1 -> ANIM_MAT_STR_TB -> STR_INF_LIST + (4 * i) + 2, tpt1 -> ENDIAN);
    
    // search for the material string on mat3 (after finding it, check its index against tpt1_anim_mat_ind)
    // I will asume that the string offsets in the mat3 string table are right. Check the string weight.
    bool mat_found = false;
    for (umax j = 0; j < mat3_mat_cnt && mat_found == false; j++) {
      mat_found = comp_strs(tpt1_anim_mat_str, mat3 -> MAT_STR_TB -> STR_CNT 
                            + get_std_uint(16, mat3 -> MAT_STR_TB -> STR_INF_LIST + (4 * j) + 2, mat3 -> ENDIAN));
      if (mat_found && j != tpt1_anim_mat_ind) // material string is found but the index position isn't correct
        soft_err = get_min_uint(soft_err, J3DGRAPH_BTP_TPT1_ERR_MAT_IND_TB);
    }
    // check if the material was found on mat3
    if (mat_found == false)
      return J3DGRAPH_BTP_TPT1_ERR_INV_MAT;
  }
  
  // reset CHAR_ARR encoding state
  set_ch_arr_enc_state(old_enc);
  
  // all checks done  
  return soft_err;
}

//~ // write_btp_tpt1_csv() function
//~ // write the CSV version of a BTP TPT1 section
//~ void write_btp_tpt1_csv(j3dgraph * btp)
//~ {
  //~ // TODO
  //~ return;
//~ }
